﻿using System.Collections.Generic;
using System.Diagnostics;

namespace TopHero
{
    public class World : ILifecycle
    {
        private Game _game;
        private Hero _hero;
        public Hero hero => _hero;

        private List<Cell> _cells;
        private List<int> _roads;
        private Dictionary<int, bool> _roadIndexDic;

        private float _width;
        private float _height;
        private int _cellNumX;
        private int _cellNumY;
        private float _cellWidth;
        private float _cellHeight;

        private PropertyManager _propertyManager;

        public World(Game game, float width, float height, int cellNumX, int cellNumY, int[] roadIndex)
        {
            _game = game;
            InitProperty();
            InitMap(width, height, cellNumX, cellNumY, roadIndex);
            InitHero();
        }

        private void InitProperty()
        {
            _propertyManager = new PropertyManager();
            Property maxLightTime = _propertyManager.AddProperty(PropertyId.MAX_LIGHT_TIME, 100);
            Property lightTimeRate = _propertyManager.AddProperty(PropertyId.LIGHT_TIME_RATE, 1);
            Property lightTime =
                _propertyManager.AddLinerProperty(PropertyId.LIGHT_TIME, 0, lightTimeRate, maxLightTime);

            Property loopNum = _propertyManager.AddProperty(PropertyId.LOOP_NUM, 0);
        }

        public float GetPropertyValue(int id)
        {
            return _propertyManager.GetValue(id);
        }

        public void AddLoopNum()
        {
            float num = GetPropertyValue(PropertyId.LOOP_NUM) + 1;
            SetPropertyValue(PropertyId.LOOP_NUM, num);
            Debug.LogFormat("当前循环次数: {0}", num);

            foreach (var cell in _cells)
            {
                MessageDispatcher.Instance.DispatchMsg(0, 0, cell.ID(), MessageType.AddLoop);
            }
        }

        internal void SetPropertyValue(int id, float value)

        {
            Property property = _propertyManager.GetProperty(id);
            property.value = value;
        }

        private void InitHero()
        {
            _hero = new Hero(_game, this);
            _hero.Start();
            EntityManager.Instance.RegisterEntity(_hero);
        }

        private void InitMap(float width, float height, int cellNumX, int cellNumY, int[] roadIndex)
        {
            _width = width;
            _height = height;
            _cellNumX = cellNumX;
            _cellNumY = cellNumY;
            _cellWidth = _width / _cellNumX;
            _cellHeight = _height / _cellNumY;

            Vector2 center = new Vector2(_width / 2.0f, _height / 2.0f);
            _cells = new List<Cell>();
            _roads = new List<int>();
            _roadIndexDic = new Dictionary<int, bool>();
            foreach (var index in roadIndex)
            {
                _roadIndexDic[index] = true;
            }

            int i = 0;
            for (int y = 0; y < _cellNumY; y++)
            {
                for (int x = 0; x < _cellNumX; x++)
                {
                    Vector2 min = new Vector2(x * _cellWidth - center.x, y * _cellHeight - center.y);
                    Vector2 max = new Vector2((x + 1) * _cellWidth - center.x, (y + 1) * _cellHeight - center.y);
                    Cell cell = new Cell(_game, min, max);

                    if (!_roadIndexDic.ContainsKey(i))
                    {
                        cell.SetEmpty();
                    }

                    _cells.Add(cell);
                    EntityManager.Instance.RegisterEntity(cell);
                    i++;
                }
            }

            for (int j = 0; j < roadIndex.Length; j++)
            {
                int index = roadIndex[j];
                Cell cell = _cells[index];
                _roads.Add(index);
                if (j == 0)
                {
                    cell.Set(new Torch(_game, cell));
                }
                else
                {
                    cell.Set(new Road(_game, cell));
                }
            }
        }


        public void Update()
        {
            _propertyManager.Update();
            CheckLightTime();
            _hero.Update();
            foreach (var cell in _cells)
            {
                cell.Update();
            }

            if (_hero.Dead())
            {
                Debug.LogFormat("英雄死亡");
            }
        }

        public void Destroy()
        {
            _hero.Destroy();
            foreach (var cell in _cells)
            {
                cell.Destroy();
            }
        }

        public int PositionToIndex(Vector2 position)
        {
            Vector2 adjustPos = position + new Vector2(_width, _height) / 2;
            int idx = (int) (_cellNumX * adjustPos.x / _width) +
                      ((int) ((_cellNumY) * adjustPos.y / _height) * _cellNumX);

            if (idx > _cells.Count - 1)
            {
                idx = _cells.Count - 1;
            }

            return idx;
        }

        public Vector2 IndexToPosition(int index)
        {
            Debug.Assert(index >= 0 && index < _cells.Count, "index >= 0 && index < _cells.Count");
            return _cells[index].position;
        }

        internal Path GetRoadPath()
        {
            List<Vector2> l = new List<Vector2>();
            foreach (var index in _roads)
            {
                Vector2 pos = IndexToPosition(index);
                l.Add(pos);
            }

            return new Path(l, true);
        }

        private void CheckLightTime()
        {
            Property maxLightTime = _propertyManager.GetProperty(PropertyId.MAX_LIGHT_TIME);
            Property lightTime = _propertyManager.GetProperty(PropertyId.LIGHT_TIME);
            if (lightTime.value >= maxLightTime.value)
            {
                lightTime.value = 0;
            }
        }

        internal Cell GetCell(int index)
        {
            Debug.Assert(index >= 0 && index < _cells.Count, "index >= 0 && index < _cells.Count");
            return _cells[index];
        }

        internal Cell GetCell(Vector2 position)
        {
            return GetCell(PositionToIndex(position));
        }
    }
}